'''
Created on 09/05/2013

@author: mirta
'''
from datetime import date

from flask import Blueprint
from flask import request
from flask import render_template
from flask import flash
from flask import redirect
from flask import url_for

from app import db

from app.security import g
from app.security import permission

from app.models.linea_base import LineaBase
from app.models.historial_lb import HistorialLineaBase
from app.models.fase import Fase
from app.models.item import Item
from app.models.permiso import Permiso

from app.wtforms.linea_base_forms import LbBuscar
from app.wtforms.linea_base_forms import LbFases
from app.wtforms.linea_base_forms import LbEditable


mod = Blueprint('lineas_base', __name__, url_prefix='/lineabase')

@mod.route('/administracion/', methods=['GET', 'POST'])
@permission('ADMINISTRACION_LINEAS_BASE')
def administracion_lb():
    """
    Muestra en pantalla las lineas base que existen en la base de datos.
    Si no recibe algun filtro como parametro, lista todas las lineas base.
    Si recibe algun filtro, muestra las lineas base que cumplen con el filtro indicado
    """
    # Si el usuario no tiene asociado ningun proyecto no se le permite ver la pagina
    if g.proyecto==None:
        flash('No tiene permiso para ver esta pagina.')
        return redirect(url_for('acceso.home'))
    
    #Obtenemos el proyecto seleccionado
    proyecto_seleccionado = g.proyecto
   
    # Obtenemos el formulario
    form =  LbBuscar(request.form)   
      
    # Obtenemos los filtros
    nombreFilter = form.nombre.data
    estadoFilter = form.estado.data
    faseFilter = form.fase.data
    
    if(nombreFilter == None):
        nombreFilter = ''
    if(estadoFilter == None):
        estadoFilter = ''
    if(faseFilter == None):
        faseFilter = '' 
    
    # Recuperamos de la base de datos las lineas base que coinciden con los filtros
    lbs = LineaBase.query.filter(LineaBase.nombre.like( '%'+nombreFilter+'%' ), LineaBase.estado.like( '%'+estadoFilter+'%' ) ).join(Fase).filter(Fase.nombre.like('%' + faseFilter + '%'), Fase.proyecto_id == proyecto_seleccionado.id)
    lbs = lbs.all()        
        
    return render_template('linea_base/administracion_lb.html', form = form, lbs = lbs)



@mod.route('/seleccionar/', methods=['GET', 'POST'])
def seleccionar_fase():
    """
    Lista las fases con estado activa en las que se pueden crear lineas base
    """
    
    #Obtenemos el proyecto seleccionado
    proyecto_seleccionado = g.proyecto
    
    # Obtenemos el formulario para listar las fases con estado Activa del proyecto
    form = LbFases(request.form)
    
    # Buscamos las fases del proyecto seleccionado que tengan estado Activa
    fases = Fase.query.filter_by(estado='Activa', proyecto_id=proyecto_seleccionado.id)
    fases = fases.all()
    
    # Si no existen fases con estado Activa entonces mandamos al usuario a la pagina de 
    # administracion
    if fases == None:
        flash('No existen fases con estado Activa en el proyecto', 'error-message')
        return redirect(url_for('lineas_base.administracion_lb', fase=fase_seleccionada))
    
    # Creamos una lista de esas fases para mostrarlos en la vista
    lista_fases = []
    for fase in fases:
        # Verificamos si el usuario tiene permisos para modificar esas fases
        if(verificar_autorizacion_fase(usuario=g.user, fase=fase, permisoRequerido='MODIFICAR')):
            # Si tiene permisos lo agregamos a la lista de fases
            lista_fases.append((fase.id, fase.nombre))
    
    # Mostramos estas fases en la interfaz
    form.fases.choices = lista_fases 
    
    # Validamos los datos ingresados en el el formulario por el usuario
    if form.validate_on_submit():
        
        # Obtenemos la fase seleccionada y lo pasamos a la pagina de creacion de lineas base
        fase_seleccionada = form.fases.data[0]
        return redirect(url_for('lineas_base.crear_lb', fase=fase_seleccionada))
    
    return render_template('linea_base/seleccionar_fase.html', form = form)
    
    

@mod.route('/crear/<fase>/', methods=['GET', 'POST'])
def crear_lb(fase):
    """
    Permite la creacion de lineas base, muestra todos los items que pueden ser incluidos en la 
    nueva linea base
    @param fase: id de la fase en la cual se creara la nueva linea base
    """
    # Obtenemos la fase
    fase_seleccionada = Fase.query.filter_by(id=fase).first()
    
    # Verificamos si el usuario tiene permisos para modificar la fase
    if(verificar_autorizacion_fase(usuario=g.user, fase=fase_seleccionada, permisoRequerido='MODIFICAR')):
        
        # Obtenemos el usuario que se ha logueado
        usuario_logueado = g.user
    
        # Obtenemos todos los items de la fase que tengan estado aprobado y no esten en ninguna linea base
        items = Item.query.filter_by(fase_id=fase, estado='Aprobado', linea_base_id=None)
        items = items.all()
    
        # Si no existen mas items disponibles para las lineas base, se informa al usuario
        if items == []:
            flash('Todos los items Aprobados de la fase se encuentran en un linea base', 'error-message')
            return redirect(url_for('lineas_base.seleccionar_fase'))
    
        else:
            # Si existen items para una nueva linea base
            # Obtenemos el formulario para la creacion de la linea base
            form = LbEditable(request.form)
    
            # Realizamos una lista para mostrar los items en la interfaz
            lista_items = []
            for item in items:
                lista_items.append((item.id, item.nombre))
    
            # Mostramos la lista de items a los usuarios    
            form.items.choices = lista_items
    
            # Validamos los datos ingresados en el formulario por los usuarios
            if form.validate_on_submit():
        
                # Obtenemos todas las lineas base de la fase
                lbs = LineaBase.query.filter_by(fase_id=fase)
                lbs = lbs.all()
        
                cont = 0
                # Comparamos que el nombre ingresado por el usuario no sea igual a uno existente
                if lbs != None:
                    for lb in lbs:
                        if form.nombre.data == lb.nombre:
                            cont = cont + 1
         
                # Si ya existe el nombre, no permitimos la creacion           
                if cont != 0:
                    flash("El nombre de la linea base ya existe en la fase, elija otro", 'error-message')
                    return redirect(url_for('lineas_base.crear_lb', fase=fase))
        
                else:
                    # Si el nombre nuevo es unico en el sistema
                    # Generamos una linea base indicando nombre, estado y fase
                    lb_nueva = LineaBase(nombre=form.nombre.data, estado='Abierta')
                    lb_nueva.fecha_creacion = date.today()
                    lb_nueva.fase = fase_seleccionada
                    # Guardamos la nueva linea base en la base de datos
                    db.session.add(lb_nueva)
                    db.session.commit()
                
                    # Guardamos el en historial la operacion realizada
                    historial_lb = HistorialLineaBase(id_lb=lb_nueva.id, operacion='Creacion',
                                                   usuario=usuario_logueado.nombre, fecha=date.today())
                    db.session.add(historial_lb)
                    db.session.commit()
                
            
                    # Obtenemos los items seleccionados por el usuario
                    items_seleccionados = form.items.data
            
                    # A cada item de la lista le agregamos su linea base correspondiente y cambiamos su estado
                    for item_id in items_seleccionados:
                        item = Item.query.filter_by(id=item_id).first()
                        item.linea_base = lb_nueva
                        item.estado = 'Bloqueado'
                        # Guardamos los cambios hechos al item en base de datos
                        db.session.add(item)
                        db.session.commit()
            
                    # Desplegamos el mensaje de exito de la operacion
                    flash('La linea base ha sido registrada con exito')
                    return redirect(url_for('lineas_base.administracion_lb'))
        
        return render_template('linea_base/crear_lb.html', form = form, fase=fase)
     
    else:
        # Si el usuario no tiene permiso para modificar la fase entonces lo redirigimos a la 
        # pagina de administracion de lineas bases y desplegamos un mensaje
        flash('NO TIENE PERMISOS PARA MODIFICAR LA FASE SELECCIONADA') 
        return redirect(url_for('lineas_base.administracion_lb'))



@mod.route('/informe/<lb>/', methods=['GET', 'POST'])
def informe_lb(lb):
    """
    Realiza un informe acerca del estado de la linea base seleccionada por el usuario, dando datos 
    sobre las operaciones que se realizaron sobre la linea base y algunos datos de los items 
    componentes de dicha linea base
    @param lb: id de la linea base seleccionada por el usuario
    """
    
    # Obtenemos la linea base seleccionada
    lb_seleccionada = LineaBase.query.filter_by(id=lb).first()
    
    # Obtenemos la fase a la cual pertenece la linea base
    fase = Fase.query.filter_by(id=lb_seleccionada.fase_id).first()
    
    # Verificamos si el usuario tiene permisos para ver datos de la fase
    if(verificar_autorizacion_fase(usuario=g.user, fase=fase, permisoRequerido='VER')):
        
        # Obtenemos los datos historicos de la linea base seleccionada
        lb_historial = HistorialLineaBase.query.filter_by(id_lb=lb)
        lb_historial = lb_historial.all()
        
        # Obtenemos los items que forman parte de la linea base seleccionada
        items = Item.query.filter_by(linea_base_id=lb)
        items = items.all()
        
        # Mostramos los datos de la linea base e items a los usuarios
        return render_template('linea_base/informe_lb.html', lb=lb,
                           historiales=lb_historial, items=items)
    
    else:
        # Si no se tiene permiso para ver los datos de la fase (informe de la linea base)
        # Redirigimos al usuario a la pagina de administracion de linea base y desplegamos un mensaje
        # informativo
        flash('NO TIENE PERMISOS PARA MODIFICAR LA FASE SELECCIONADA') 
        return redirect(url_for('lineas_base.administracion_lb'))
        


@mod.route('/eliminar/<lb>/', methods=['GET', 'POST'])
def eliminar_lb(lb):
    """
    Permite la eliminacion de lineas base que existen en el sistema
    @param lb: id de la linea base que se  va a eliminar
    """
    
    # Obtenemos el usuario que se ha logueado
    usuario_logueado = g.user
    
    # Obtenemos la linea base seleccionada por el usuario
    lb_seleccionada = LineaBase.query.filter_by(id=lb).first()
    #flash(lb_seleccionada)
    #return redirect(url_for('lineas_base.administracion_lb'))
    
    # Obtenemos la fase a la cual pertenece la linea base seleccionada
    fase = Fase.query.filter_by(id=lb_seleccionada.fase_id).first()
    
    # Verificamos si el usuario tiene permisos para modificar lla fase
    if(verificar_autorizacion_fase(usuario=g.user, fase=fase, permisoRequerido='MODIFICAR')):
        
        # Si la fase de la linea base tiene estado Activa se podra realizar la eliminacion
        if lb_seleccionada.fase.estado == 'Activa':
            
            # Si la linea base seleccionada esta con estado Eliminada
            if lb_seleccionada.estado == 'Eliminada':
                # Entonces se redirige al usuario a la pagina de administracion de linea base
                # y se despliega un mensaje indicando el hecho
                flash('La linea base ya se encuentra con estado Eliminada')
                return redirect(url_for('lineas_base.administracion_lb'))
            
            # Establecemos el estado de la linea base como eliminada
            lb_seleccionada.estado = 'Eliminada'
            db.session.add(lb_seleccionada)
            db.session.commit()
        
            # Guardamos el en historial la operacion realizada
            historial_lb = HistorialLineaBase(id_lb=lb_seleccionada.id, operacion='Eliminacion',
                                                   usuario=usuario_logueado.nombre, fecha=date.today())
            db.session.add(historial_lb)
            db.session.commit()
            
            # Obtenemos todos los items que se encuentran en la linea base eliminada
            items = Item.query.filter_by(linea_base_id=lb)
            items = items.all()
            
            # Por cada item
            for item in items:
                # Establecemos su estado a Aprobado
                item.estado = 'Aprobado'
                item.linea_base_id = None
                item.linea_base = None
                db.session.add(item)
                db.session.commit()
                
                # Obtenemos todos los items cuyos padres se encuentren en la linea base eliminada
                items_hijos = Item.query.filter_by(padre_id=item.id)
                items_hijos = items_hijos.all()
                # Si existen hijos
                if items_hijos!=None:
                    # Por cada uno de esos hijos
                    for hijo in items_hijos:
                        # Cambiamos su estado a revision
                        hijo.estado = 'Revision'
                        db.session.add(hijo)
                        db.session.commit()
                        
                        # Si ese hijo esta en una linea base y ella no tienen estado de Revision
                        if hijo.linea_base.id!=None and hijo.linea_base.estado!='Revision':
                            # Pasamos la linea base para que sea procesada
                            cambiar_estado(hijo.linea_base.id)     
                
                # Obtenemos todos los items cuyos antecesores se encuentren en la linea base eliminada
                items_sucesores = Item.query.filter_by(antecesor_id=item.id)
                items_sucesores = items_sucesores.all()
                # Si existen sucesores
                if items_sucesores!=None:
                    # Por cada uno de esos sucesores
                    for sucesor in items_sucesores:
                        # Cambiamos su estado a revision
                        sucesor.estado = 'Revision'
                        db.session.add(sucesor)
                        db.session.commit()
                        
                        # Si ese sucesor esta en una linea base y ella no esta en estado de revision
                        if sucesor.linea_base.id!=None and sucesor.linea_base.estado!='Revision':
                            # Pasamos esa linea base para que sea procesada
                            cambiar_estado(sucesor.linea_base.id) 
            
            # Indicamos que la eliminacion fue realizada correctamente y redirigimos al usuario
            # a la pagina de administracion de lineas base
            flash('La linea base ha sido eliminada con exito')
            return redirect(url_for('lineas_base.administracion_lb'))
        
        else:
            # Si la fase de la linea base seleccionada no esta con estado Activo
            # desplegamos un mensaje informando al usuario que no se puede realizar la eliminacion
            # debido a esa situacion y lo redirigimos a la pagina de administacion de lineas base
            flash('La fase de la linea base seleccionada no esta Activa')
            return redirect(url_for('lineas_base.administracion_lb'))
    else:
        # Si no se tiene permiso para modificar la fase de la linea base, desplegamos un mensaje
        # indicando al usuario y lo redirigimos a la pagina de administracion de lineas base
        flash('NO TIENE PERMISOS PARA MODIFICAR LA FASE SELECCIONADA') 
        return redirect(url_for('lineas_base.administracion_lb'))
        
        
        
def cambiar_estado(lb):  
    """
    Cambia el estado de las lineas base e items de la misma relacionados a la linea base eliminada
    al estado de Revision
    @param lb: id de la linea base que se va a poner en revision
    """
    # Obtenemos el usuario que se ha logueado
    usuario_logueado = g.user
    
    # Obtenemos la linea base que sera procesada
    linea_base = LineaBase.query.filter_by(id=lb).first()
    # Cambiamos su estado a revision
    linea_base.estado = 'Revision'
    db.session.add(linea_base)
    db.session.commit()
    
    # Guardamos el en historial la operacion realizada
    historial_lb = HistorialLineaBase(id_lb=linea_base.id, operacion='Pasa a revision por eliminacion de la linea base: '+linea_base.nombre,
                                                   usuario=usuario_logueado.nombre, fecha=date.today())
    db.session.add(historial_lb)
    db.session.commit()
    
    # Obtenemos los items que se encuentran en esa linea base
    items = Item.query.filter_by(linea_base_id=lb)
    items = items.all()
    
    # Si existen items
    if items!=None:
        # Por cada uno de esos items
        for item in items:
            # Cambiamos su estado a revision
            item.estado = 'Revision'
            db.session.add(item)
            db.session.commit()
            
            # Obtenemos todo los items que tengan padres en la linea base
            items_hijos = Item.query.filter_by(padre_id=item.id)
            items_hijos = items_hijos.all()
            # Si existen hijos
            if items_hijos!=None:
                # Por cada uno de esos hijos
                for hijo in items_hijos:
                    # Cambiamos su estado a revision
                    hijo.estado = 'Revision'
                    db.session.add(hijo)
                    db.session.commit()
                    
                    # Si un hijo esta en una linea base y esta no tiene estado de revision
                    if hijo.linea_base.id!=None and hijo.linea_base.estado!='Revision':
                        # Pasamos la linea base para que la procesen
                        cambiar_estado(hijo.linea_base.id)     
            
            # Obtenemos todos los items que tengan antecesores en la linea base
            items_sucesores = Item.query.filter_by(antecesor_id=item.id)
            items_sucesores = items_sucesores.all()
            # Si existen sucesores
            if items_sucesores!=None:
                # Por cada uno de esos sucesores
                for sucesor in items_sucesores:
                    # Cambiamos su estado a revision
                    sucesor.estado = 'Revision'
                    db.session.add(sucesor)
                    db.session.commit()
                    
                    # Si un sucesor esta en una linea base y esta no tiene estado de revision
                    if sucesor.linea_base.id!=None and sucesor.linea_base.estado!='Revision':
                        # Pasamos la linea base para que la procesen
                        cambiar_estado(sucesor.linea_base.id) 
                        
                        
                        
def verificar_autorizacion_fase(usuario = None, fase = None, permisoRequerido = None):
    """
    Verifica que el usuario posea pemisos para realizar una operacion determinada sobre una
    fase
    @param usuario: usuario que se logueo
    @param fase: fase sobre la cual se desea realizar una operacion
    @param permisoRequerido: permiso que debe poseer el usuario sobre la fase para que pueda
    realizar la acccion
    """
    # Establecemos por defecto que el usuario logueado no esta autorizado para realizar la  operacion
    autorizado = False
    
    #============================================================================
    #  rolAdministrador = db.session.query(Rol).filter_by(nombre = 'Administrador de Sistema').first()
    # 
    #  if rolAdministrador in usuario.roles:
    #          autorizado = True
    #          
    #  else:         
    #============================================================================
    
    # Obtenemos el permisos con los elementos requeridos
    permiso = db.session.query(Permiso).filter_by(nombre = permisoRequerido, entidad_id = fase.entidad_id).first()
    # flash(permiso)
    
    # Si existe dicho permiso
    if(permiso != None):
        # Por cada rol que posea el usuario logueado
        for rol in usuario.roles:
            # Si tiene ese permiso
            if permiso in rol.permiso:
                # Establecemos que esta autorizado para realizar la operacion
                autorizado = True
    
    return autorizado
    